package cn.kinoko.service.user.impl;

import cn.kinoko.common.aspect.annotation.CacheEvict;
import cn.kinoko.common.aspect.annotation.Cacheable;
import cn.kinoko.common.base.model.BaseServiceImpl;
import cn.kinoko.common.code.ErrorCodeEnum;
import cn.kinoko.common.constant.CacheKeyConstant;
import cn.kinoko.common.utils.BCrypt;
import cn.kinoko.common.utils.BeanHelper;
import cn.kinoko.common.utils.ExceptionUtil;
import cn.kinoko.mapper.user.UserInfoMapper;
import cn.kinoko.model.user.constant.IdentityTypeEnum;
import cn.kinoko.model.user.dto.UserInfoDto;
import cn.kinoko.model.user.entity.UserIdentityInfo;
import cn.kinoko.model.user.entity.UserInfo;
import cn.kinoko.model.user.req.PassChangeReq;
import cn.kinoko.model.user.req.UserInfoSaveReq;
import cn.kinoko.model.user.req.UserInfoUpdateReq;
import cn.kinoko.service.common.util.AuthUtil;
import cn.kinoko.service.user.UserIdentityInfoService;
import cn.kinoko.service.user.UserInfoService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 用户基础信息服务实现类
 *
 * @Author: kk
 * @Date: 2023/2/7  20:53
 * @Version 1.0
 */
@Slf4j
@Service
public class UserInfoServiceImpl extends BaseServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Resource
    private UserIdentityInfoService userIdentityInfoService;

    /**
     * 验重
     *
     * @param phone 手机号
     * @param email 邮箱
     * @return 是否重复
     */
    @Override
    public boolean checkRepeat(String phone, String email, Long uid) {
        if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(email)) {
            return false;
        }
        return userIdentityInfoService.lambdaQuery()
                .ne(uid != null, UserIdentityInfo::getUid, uid)
                .eq(StringUtils.isNotBlank(phone), UserIdentityInfo::getIdentifier, phone)
                .eq(StringUtils.isNotBlank(email), UserIdentityInfo::getIdentifier, email)
                .count() > 0;
    }

    /**
     * 根据手机/邮箱查询用户
     *
     * @param account 手机/邮箱
     * @return 用户信息
     */
    @Override
    public UserInfoDto getUserInfoDtoByAccount(String account) {
        if (StringUtils.isBlank(account)) {
            return null;
        }
        UserIdentityInfo identityInfo = getIdentityInfo(account);
        if (identityInfo == null) {
            return null;
        }
        // 根据身份信息获取用户信息
        UserInfoDto userInfo = BeanHelper.copyProperties(getById(identityInfo.getUid()), UserInfoDto.class);
        if (userInfo == null) {
            return null;
        }
        // 设置密码
        userInfo.setPassword(identityInfo.getCredential());
        return userInfo;
    }

    /**
     * 保存用户信息
     *
     * @param userInfo 用户信息
     * @return 执行结果
     */
    @Transactional
    @Override
    public boolean save(UserInfoSaveReq userInfo) {
        if (userInfo == null) {
            return false;
        }
        // 将用户信息保存到UserInfo对象中
        UserInfo userInfoAdd = BeanHelper.copyProperties(userInfo, UserInfo.class);
        if (userInfoAdd == null) {
            return false;
        }
        // 保存用户基础信息
        save(userInfoAdd);
        // 保存用户授权信息
        saveUserIdentityInfo(userInfo, userInfoAdd.getUid());
        return true;
    }

    /**
     * 更新用户信息
     *
     * @param currentUser 当前用户
     * @param req         用户信息
     * @return 执行结果
     */
    @Override
    @CacheEvict(key = CacheKeyConstant.USER_INFO_KEY, params = "#currentUser.uid")
    public boolean update(UserInfo currentUser, UserInfoUpdateReq req) {
        UserInfo user = AuthUtil.getCurrentUser();
        // 校验手机是否重复
        if (req.getPhone() != null) {
            boolean emailRepeat = checkRepeat(req.getPhone(), null, user.getUid());
            ExceptionUtil.assertion(emailRepeat, ErrorCodeEnum.USER_ERROR_A0155);
            user.setPhone(req.getPhone());
        }
        // 校验邮箱是否重复
        if (req.getEmail() != null) {
            boolean emailRepeat = checkRepeat(null, req.getEmail(), user.getUid());
            ExceptionUtil.assertion(emailRepeat, ErrorCodeEnum.USER_ERROR_A0154);
            user.setEmail(req.getEmail());
        }
        user.setUserName(req.getUserName());
        // 更新
        return updateById(user);
    }

    /**
     * 获取用户信息
     *
     * @param uid uid
     * @return 用户信息
     */
    @Cacheable(key = CacheKeyConstant.USER_INFO_KEY, params = "#uid")
    @Override
    public UserInfo getUser(Long uid) {
        return getById(uid);
    }

    /**
     * 修改密码
     *
     * @param req 请求
     * @return 执行结果
     */
    @Override
    public boolean changePass(PassChangeReq req) {
        UserInfo currentUser = AuthUtil.getCurrentUser();
        // 校验旧密码
        UserInfoDto userInfoDto = getUserInfoDtoByAccount(currentUser.getEmail());
        boolean match = BCrypt.checkpw(req.getOldPass(), userInfoDto.getPassword());
        ExceptionUtil.assertion(!match, ErrorCodeEnum.USER_ERROR_A0123);
        // 更新密码
        return updatePwd(currentUser.getUid(), req.getPass(), IdentityTypeEnum.PHONE_EMAIL);
    }

    /**
     * 重置密码
     *
     * @param email  邮箱
     * @param newPwd 新密码
     * @return 执行结果
     */
    @Override
    public boolean resetPass(String email, String newPwd) {
        // 根据邮箱获取用户信息
        UserInfoDto userInfoDto = getUserInfoDtoByAccount(email);
        return updatePwd(userInfoDto.getUid(), newPwd, IdentityTypeEnum.PHONE_EMAIL);
    }

    /**
     * 更新密码
     *
     * @param uid      用户id
     * @param newPwd   新密码
     * @param typeEnum 认证类型
     * @return 执行结果
     */
    private boolean updatePwd(Long uid, String newPwd, IdentityTypeEnum typeEnum) {
        // 加密密码
        String salt = BCrypt.gensalt();
        newPwd = BCrypt.hashpw(newPwd, salt);
        // 更新密码
        return userIdentityInfoService.lambdaUpdate()
                .eq(UserIdentityInfo::getUid, uid)
                .eq(UserIdentityInfo::getIdentityType, typeEnum.getCode())
                .set(UserIdentityInfo::getCredential, newPwd)
                .update();
    }

    /**
     * 保存用户授权信息
     *
     * @param userInfo 用户信息
     * @param uid      用户id
     */
    private void saveUserIdentityInfo(UserInfoSaveReq userInfo, Long uid) {
        List<UserIdentityInfo> userIdentityInfoAddList = new ArrayList<>();
        if (StringUtils.isNotBlank(userInfo.getEmail())) {
            // 创建用户授权信息对象并添加到列表中
            userIdentityInfoAddList.add(buildUserIdentityInfo(uid, userInfo.getEmail(), userInfo.getPassword(), IdentityTypeEnum.PHONE_EMAIL));
        }
        if (StringUtils.isNotBlank(userInfo.getPhone())) {
            // 创建用户授权信息对象并添加到列表中
            userIdentityInfoAddList.add(buildUserIdentityInfo(uid, userInfo.getPhone(), userInfo.getPassword(), IdentityTypeEnum.PHONE_EMAIL));
        }
        if (CollectionUtils.isNotEmpty(userIdentityInfoAddList)) {
            // 批量保存用户授权信息
            userIdentityInfoService.saveBatch(userIdentityInfoAddList);
        }
    }

    /**
     * 根据给定的参数构建UserIdentityInfo对象
     *
     * @param uid          用户id
     * @param identifier   用户标识
     * @param credential   用户凭证
     * @param identityType 用户身份类型
     * @return 构建好的UserIdentityInfo对象
     */
    private UserIdentityInfo buildUserIdentityInfo(Long uid, String identifier, String credential, IdentityTypeEnum identityType) {
        UserIdentityInfo userIdentityAdd = new UserIdentityInfo();
        userIdentityAdd.setUid(uid);
        userIdentityAdd.setIdentityType(identityType.getCode());
        userIdentityAdd.setIdentifier(identifier);
        userIdentityAdd.setCredential(credential);
        return userIdentityAdd;
    }

    /**
     * 根据给定的账号获取用户身份信息
     *
     * @param account 账号
     * @return 用户身份信息
     */
    private UserIdentityInfo getIdentityInfo(String account) {
        // 查询用户身份授权信息
        List<UserIdentityInfo> identityList = userIdentityInfoService.lambdaQuery()
                .eq(UserIdentityInfo::getIdentityType, IdentityTypeEnum.PHONE_EMAIL.getCode())
                .eq(UserIdentityInfo::getIdentifier, account)
                .list();
        if (CollectionUtils.isEmpty(identityList)) {
            return null;
        }
        // 获取带有凭证的信息
        return identityList.stream()
                .filter(userIdentityInfo -> StringUtils.isNotBlank(userIdentityInfo.getCredential()))
                .findAny().orElseGet(() -> {
                    // 如果没有带有凭证的信息，则抛出异常
                    ExceptionUtil.throwOut(ErrorCodeEnum.USER_ERROR_A0156);
                    return null;
                });
    }


}
